# pylint: disable=invalid-name
# Requires Python 3.6+
# Ref: https://www.sphinx-doc.org/en/master/usage/configuration.html
"""Configuration for the Sphinx documentation generator."""

import sys
from pathlib import Path
from functools import partial

from setuptools_scm import get_version


# -- Path setup --------------------------------------------------------------

PROJECT_ROOT_DIR = Path(__file__).parents[1].resolve()  # pylint: disable=no-member
get_scm_version = partial(get_version, root=PROJECT_ROOT_DIR)

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.


sys.path.insert(0, str(PROJECT_ROOT_DIR))

# Make in-tree extension importable in non-tox setups/envs, like RTD.
# Refs:
# https://github.com/readthedocs/readthedocs.org/issues/6311
# https://github.com/readthedocs/readthedocs.org/issues/7182
sys.path.insert(0, str((Path(__file__).parent / '_ext').resolve()))

# -- Project information -----------------------------------------------------

github_url = 'https://github.com'
github_repo_org = 'abhinavsingh'
github_repo_name = 'proxy.py'
github_repo_slug = f'{github_repo_org}/{github_repo_name}'
github_repo_url = f'{github_url}/{github_repo_slug}'
github_sponsors_url = f'{github_url}/sponsors'

project = github_repo_name.title()
author = f'{project} project contributors'
copyright = author  # pylint: disable=redefined-builtin

# The short X.Y version
version = '.'.join(
    get_scm_version(
        local_scheme='no-local-version',
    ).split('.')[:3],
)

# The full version, including alpha/beta/rc tags
release = get_scm_version()

rst_epilog = f"""
.. |project| replace:: {project}
.. |release_l| replace:: ``v{release}``
"""


# -- General configuration ---------------------------------------------------


# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'

# The reST default role (used for this markup: `text`) to use for all
# documents.
# Ref: python-attrs/attrs#571
default_role = 'any'

# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True

# The name of the Pygments (syntax highlighting) style to use.
# pygments_style = 'sphinx'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    # stdlib-party extensions:
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.extlinks',
    'sphinx.ext.intersphinx',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.viewcode',

    # Third-party extensions:
    'myst_parser',  # extended markdown; https://pypi.org/project/myst-parser/
    'sphinxcontrib.apidoc',
    'sphinxcontrib.towncrier',  # provides `towncrier-draft-entries` directive
]

# Conditional third-party extensions:
try:
    import sphinxcontrib.spelling as _sphinxcontrib_spelling
except ImportError:
    extensions.append('spelling_stub_ext')
else:
    del _sphinxcontrib_spelling
    extensions.append('sphinxcontrib.spelling')

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
    'changelog-fragments.d/**',  # Towncrier-managed change notes
]


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'furo'

html_show_sphinx = True

html_theme_options = {
}

html_context = {
}


# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
# html_title = f'{project} Documentation'

# A shorter title for the navigation bar.  Default is the same as html_title.
# html_short_title = 'Documentation'

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = f'https://{github_repo_name.replace(".", "")}.readthedocs.io/en/latest/'

# The master toctree document.
root_doc = master_doc = 'index'  # Sphinx 4+ / 3-  # noqa: WPS429


# -- Extension configuration -------------------------------------------------

# -- Options for intersphinx extension ---------------------------------------

intersphinx_mapping = {
    'myst': ('https://myst-parser.rtfd.io/en/latest', None),
    'python': ('https://docs.python.org/3', None),
    'python2': ('https://docs.python.org/2', None),
}

# -- Options for todo extension ----------------------------------------------

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for sphinxcontrib.apidoc extension ------------------------------

apidoc_excluded_paths = [
    'plugin/cache/*',
    'testing/*.py',
]
apidoc_extra_args = [
    '--implicit-namespaces',
    '--private',  # include “_private” modules
]
apidoc_module_dir = str(PROJECT_ROOT_DIR / 'proxy')
apidoc_module_first = False
apidoc_output_dir = 'pkg'
apidoc_separate_modules = True
apidoc_toc_file = None

# -- Options for sphinxcontrib.spelling extension ----------------------------

spelling_ignore_acronyms = True
spelling_ignore_importable_modules = True
spelling_ignore_pypi_package_names = True
spelling_ignore_python_builtins = True
spelling_ignore_wiki_words = True
spelling_show_suggestions = True
spelling_word_list_filename = [
    'spelling_wordlist.txt',
]

# -- Options for extlinks extension ------------------------------------------

extlinks = {
    'issue': (f'{github_repo_url}/issues/%s', '#'),  # noqa: WPS323
    'pr': (f'{github_repo_url}/pull/%s', 'PR #'),  # noqa: WPS323
    'commit': (f'{github_repo_url}/commit/%s', ''),  # noqa: WPS323
    'gh': (f'{github_url}/%s', 'GitHub: '),  # noqa: WPS323
    'user': (f'{github_sponsors_url}/%s', '@'),  # noqa: WPS323
}

# -- Options for linkcheck builder -------------------------------------------

linkcheck_ignore = [
    # local URLs
    r'http://localhost:\d+/',
    # GHA sees "403 Client Error: Forbidden for url:"
    # while the URL actually works
    r'https://developers.cloudflare.com/',
]
linkcheck_workers = 25

# -- Options for towncrier_draft extension -----------------------------------

towncrier_draft_autoversion_mode = 'draft'  # or: 'sphinx-version', 'sphinx-release'
towncrier_draft_include_empty = True
towncrier_draft_working_directory = PROJECT_ROOT_DIR
# Not yet supported: towncrier_draft_config_path = 'pyproject.toml'  # relative to cwd

# -- Options for myst_parser extension ------------------------------------------

myst_enable_extensions = [
    'colon_fence',  # allow to optionally use ::: instead of ```
    'deflist',
    'html_admonition',  # allow having HTML admonitions
    'html_image',  # allow HTML <img> in Markdown
    # FIXME: `linkify` turns "Proxy.Py` into a link so it's disabled now
    # Ref: https://github.com/executablebooks/MyST-Parser/issues/428#issuecomment-970277208
    # "linkify",  # auto-detect URLs @ plain text, needs myst-parser[linkify]
    'replacements',  # allows Jinja2-style replacements
    'smartquotes',  # use "cursive" quotes
    'substitution',  # replace common ASCII shortcuts into their symbols
]
myst_substitutions = {
  'project': project,
  'release': release,
  'release_l': f'`v{release}`',
  'version': version,
}
myst_heading_anchors = 3

# -- Strict mode -------------------------------------------------------------

# The reST default role (used for this markup: `text`) to use for all
# documents.
# Ref: python-attrs/attrs#571
default_role = 'any'

nitpicky = True
_any_role = 'any'
_py_obj_role = 'py:obj'
_py_class_role = 'py:class'
nitpick_ignore = [
    (_any_role, '<proxy.HttpProxyBasePlugin>'),
    (_any_role, '__init__'),
    (_any_role, 'Client'),
    (_any_role, 'event_queue'),
    (_any_role, 'fd_queue'),
    (_any_role, 'flag.flags'),
    (_any_role, 'flags.work_klass'),
    (_any_role, 'flush'),
    (_any_role, 'httpx'),
    (_any_role, 'HttpParser.state'),
    (_any_role, 'HttpProtocolHandler'),
    (_any_role, 'multiprocessing.Manager'),
    (_any_role, 'proxy.core.base.tcp_upstream.TcpUpstreamConnectionHandler'),
    (_any_role, 'work_klass'),
    (_py_class_role, '_asyncio.Task'),
    (_py_class_role, 'asyncio.events.AbstractEventLoop'),
    (_py_class_role, 'BaseListener'),
    (_py_class_role, 'CacheStore'),
    (_py_class_role, 'Channel'),
    (_py_class_role, 'HttpParser'),
    (_py_class_role, 'HttpProtocolHandlerPlugin'),
    (_py_class_role, 'HttpProxyBasePlugin'),
    (_py_class_role, 'HttpWebServerBasePlugin'),
    (_py_class_role, 'multiprocessing.context.Process'),
    (_py_class_role, 'multiprocessing.synchronize.Lock'),
    (_py_class_role, 'NonBlockingQueue'),
    (_py_class_role, 'paramiko.channel.Channel'),
    (_py_class_role, 'proxy.http.parser.parser.T'),
    (_py_class_role, 'proxy.plugin.cache.store.base.CacheStore'),
    (_py_class_role, 'proxy.core.pool.AcceptorPool'),
    (_py_class_role, 'proxy.core.executors.ThreadlessPool'),
    (_py_class_role, 'proxy.core.work.threadless.T'),
    (_py_class_role, 'proxy.core.work.work.T'),
    (_py_class_role, 'proxy.core.acceptor.threadless.Threadless'),
    (_py_class_role, 'queue.Queue[Any]'),
    (_py_class_role, 'SelectableEvents'),
    (_py_class_role, 'TcpClientConnection'),
    (_py_class_role, 'TcpServerConnection'),
    (_py_class_role, 'unittest.case.TestCase'),
    (_py_class_role, 'unittest.result.TestResult'),
    (_py_class_role, 'UUID'),
    (_py_class_role, 'UpstreamConnectionPool'),
    (_py_class_role, 'HttpClientConnection'),
    (_py_class_role, 'Url'),
    (_py_class_role, 'WebsocketFrame'),
    (_py_class_role, 'Work'),
    (_py_class_role, 'proxy.core.acceptor.work.Work'),
    (_py_class_role, 'connection.Connection'),
    (_py_class_role, 'EventQueue'),
    (_py_class_role, 'T'),
    (_py_class_role, 'HostPort'),
    (_py_class_role, 'TcpOrTlsSocket'),
    (_py_class_role, 're.Pattern'),
    (_py_obj_role, 'proxy.core.work.threadless.T'),
    (_py_obj_role, 'proxy.core.work.work.T'),
    (_py_obj_role, 'proxy.core.base.tcp_server.T'),
    (_py_obj_role, 'proxy.core.work.fd.fd.T'),
]
